using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;

namespace SpellCore
{
    public class AggregatedSpellSession: ISpellChecker
    {
       // private SimpleFileBasedWordContainer _goodWords;
        
        private static ISpellCheckEngineFactory _engineFactory;
        private IEnumerable<ISpellCheckEngine> _spellCheckEngines;
        private readonly string _writingSystemId;

        public AggregatedSpellSession(string writingSystemId)
        {
            if (String.IsNullOrEmpty(writingSystemId))
            {
                throw new ArgumentException("writingSystemId cannot be empty");
            }
            writingSystemId = writingSystemId.ToLowerInvariant();
            _spellCheckEngines = EngineFactory.GetEnginesForWritingSystem(writingSystemId);

            _writingSystemId = writingSystemId;
           // _goodWords = new SimpleFileBasedWordContainer(BaseDirectory, writingSystemId,"CorrectWords.txt");
        }

        public bool HasEngines
        {
            get
            {
                foreach (ISpellCheckEngine engine in _spellCheckEngines)
                {
                    return true;
                }
                return false;
            }
        }

        public void CreateDefaultEngine()
        {
            if(HasEngines)
            {
                throw new InvalidOperationException("Only create default engine when there are no engines");
            }
            AscensWrapper.CreateDictionary(HardCodedPathProviderFactory.PathToEngineRootDirectory,
                Path.Combine(HardCodedPathProviderFactory.SuppliedDictionaryBaseDirectory, _writingSystemId),
                Path.Combine(HardCodedPathProviderFactory.UserDictionaryBaseDirectory, _writingSystemId),
                _writingSystemId);
            _spellCheckEngines = EngineFactory.GetEnginesForWritingSystem(_writingSystemId);
        }
        
        internal static ISpellCheckEngineFactory EngineFactory
        {
            get
            {
                if(_engineFactory == null)
                {
                    _engineFactory = new HardCodedPathProviderFactory();
                }
                return _engineFactory;
            }
            set { _engineFactory = value; }
        }

        public void AddGoodWord(string word)
        {
            foreach (ISpellCheckEngine engine in _spellCheckEngines)
            {
                if (engine.CanSupportAdditions())
                {
                    engine.AddWordAsGood(word);
                }
            }
        }

        #region ISpellChecker Members

        public void AddBadWord(string word)
        {
            foreach (ISpellCheckEngine engine in _spellCheckEngines)
            {
                if (engine.CanSupportBadWords())
                {
                    engine.AddWordAsBad(word);
                }
            }
        }

        #endregion

        public CheckingResult GetWordStatus(String word)
        {
            //if (_goodWords.Contains(word))
            //{
            //    return CheckingResult.Good;
            //}
            CheckingResult mostSpecificResult = CheckingResult.Unknown;
            foreach (ISpellCheckEngine engine in _spellCheckEngines)
            {
                CheckingResult result = engine.GetWordStatus(word);
                if (result > mostSpecificResult)
                {
                    mostSpecificResult = result;
                }
            }
            return mostSpecificResult;
        }

        public IEnumerator InteropGetSuggestions(string word)
        {
            return GetSuggestions(word).GetEnumerator();
        }
        public IEnumerable<SimilarityCheckResult> GetSuggestions(string word)
        {
            List<SimilarityCheckResult> setOfAggregatedSuggestions = new List<SimilarityCheckResult>();
            List<Queue<SimilarityCheckResult>> setOfSuggestionLists = new List<Queue<SimilarityCheckResult>>();
            

            foreach (ISpellCheckEngine engine in _spellCheckEngines)
            {
                if (engine.CanProvideSuggestions())
                {
                    Queue<SimilarityCheckResult> suggestions = new Queue<SimilarityCheckResult>();
                    foreach (SimilarityCheckResult result in engine.GetSuggestions(word))
                    {
                        suggestions.Enqueue(result);
                    }
                    setOfSuggestionLists.Add(suggestions);
                }
            }
            //use a dictionary to keep track of empty queues since we can't remove them while
            //iterating over the queue
            Dictionary<Queue<SimilarityCheckResult>,bool> emptyQueues = new Dictionary<Queue<SimilarityCheckResult>, bool>();
            
            int numberOfEmptyQueues = 0;
            //once all queues of suggestions have been emptied, we can stop looping
            while (numberOfEmptyQueues < setOfSuggestionLists.Count)
            {
                foreach (Queue<SimilarityCheckResult> queue in setOfSuggestionLists)
                {

                    if (queue.Count == 0)
                    {
                        if (!emptyQueues.ContainsKey(queue))
                        {
                            numberOfEmptyQueues++;
                            emptyQueues.Add(queue, true);
                        }
                        continue;
                    }
                    SimilarityCheckResult result = queue.Dequeue();
                    if (!setOfAggregatedSuggestions.Contains(result))
                    {
                        setOfAggregatedSuggestions.Add(result);
                    }                  
                }
            }
            return setOfAggregatedSuggestions;
        }

    }

}